
#ifndef UTILS_BOUNDS
#define UTILS_BOUNDS

class Bounds3f {
public:
    Vector3f pMin, pMax;

    Bounds3f() {
        double minNum = std::numeric_limits<double>::lowest();
        double maxNum = std::numeric_limits<double>::max();
        pMax = Vector3f(minNum, minNum, minNum);
        pMin = Vector3f(maxNum, maxNum, maxNum);
    }
    Bounds3f(const Vector3f& p)
        : pMin(p), pMax(p) {}
    Bounds3f(const Vector3f& p1, const Vector3f& p2)
        : pMin(MinEachComponent(p1, p2)), pMax(MaxEachComponent(p1, p2)) {}

    Vector3f& operator[](const int i) { return (i == 0) ? pMin : pMax; }
};


static Vector3f Diagonal(const Bounds3f& b) {
    return b.pMax - b.pMin;
}

static int MaxDimension(const Bounds3f& b) {
    return MaxDimension(Diagonal(b));
}

static double SurfaceArea(const Bounds3f& b) {
    auto d = Diagonal(b);
    return 2 * (d.x * d.y + d.x * d.z + d.y * d.z);
}

static Vector3f Centroid(const Bounds3f& b) {
    return 0.5f * b.pMin + 0.5f * b.pMax;
}

static bool Intersect(const Ray& ray, const Bounds3f& b, int is_dir_neg[3]) {
    float min_x = (b.pMin.x - ray.origin.x) * ray.invDirection[0];
    float max_x = (b.pMax.x - ray.origin.x) * ray.invDirection[0];

    float min_y = (b.pMin.y - ray.origin.y) * ray.invDirection[1];
    float max_y = (b.pMax.y - ray.origin.y) * ray.invDirection[1];

    float min_z = (b.pMin.z - ray.origin.z) * ray.invDirection[2];
    float max_z = (b.pMax.z - ray.origin.z) * ray.invDirection[2];

    //如果方向为负（反向），就交换最早和最晚时间
    if (is_dir_neg[0]) std::swap(min_x, max_x);
    if (is_dir_neg[1]) std::swap(min_y, max_y);
    if (is_dir_neg[2]) std::swap(min_z, max_z);

    float enter = std::max(min_x, std::max(min_y, min_z));
    float exit = std::min(max_x, std::min(max_y, max_z));

    if (enter < exit && exit >= 0)
        return true;
    else return false;
}

static Bounds3f Intersect(const Bounds3f& b1, const Bounds3f& b2) {
    return Bounds3f(MaxEachComponent(b1.pMin, b2.pMin), MinEachComponent(b1.pMax, b2.pMax));
}

static Vector3f Offset(const Bounds3f& b, const Vector3f& p) {
    Vector3f o = p - b.pMin;
    if (b.pMax.x > b.pMin.x)
        o.x /= b.pMax.x - b.pMin.x;
    if (b.pMax.y > b.pMin.y)
        o.y /= b.pMax.y - b.pMin.y;
    if (b.pMax.z > b.pMin.z)
        o.z /= b.pMax.z - b.pMin.z;
    return o;
}

static bool Overlaps(const Bounds3f& b1, const Bounds3f& b2) {
    bool x = (b1.pMax.x >= b2.pMin.x) && (b1.pMin.x <= b2.pMax.x);
    bool y = (b1.pMax.y >= b2.pMin.y) && (b1.pMin.y <= b2.pMax.y);
    bool z = (b1.pMax.z >= b2.pMin.z) && (b1.pMin.z <= b2.pMax.z);
    return (x && y && z);
}

static bool Inside(const Bounds3f& b, const Vector3f& p) {
    return (p.x >= b.pMin.x && p.x <= b.pMax.x && p.y >= b.pMin.y && p.y <= b.pMax.y && p.z >= b.pMin.z && p.z <= b.pMax.z);
}

static Bounds3f Union(const Bounds3f& b1, const Bounds3f& b2) {
    return Bounds3f(MinEachComponent(b1.pMin, b2.pMin), MaxEachComponent(b1.pMax, b2.pMax));
}

static Bounds3f Union(const Bounds3f& b, const Vector3f& p) {
    return Bounds3f(MinEachComponent(b.pMin, p), MaxEachComponent(b.pMax, p));
}

#endif // UTILS_BOUNDS
